package com.sardine.base.esm.mapper;

import com.sardine.base.esm.session.EsmSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.env.Environment;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.Assert;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Set;

/**
 * 上海京东到家元信信息技术有限公司<br>
 * Created with IntelliJ IDEA.
 * Date:2017/5/23
 * Time:15:02
 * 自动扫描的核心类，扫描class path 下与该包名的路径下找到符合条件的类并注册为spring的一个bean
 * @author daifeng<br>
 *         daifeng@imdada.cn
 * @since JDK 1.7 <br>
 * Copyright (c) 2017 imdada System Incorporated All Rights Reserved.
 */
public class ClassPathEsmMapperScanner extends ClassPathBeanDefinitionScanner {
    private static final Logger logger = LoggerFactory.getLogger(ClassPathEsmMapperScanner.class);

    private EsmSessionFactory esmSessionFactory;

    private Class<? extends Annotation> annotationClass;

    private Class<?> markerInterface;

    public ClassPathEsmMapperScanner(BeanDefinitionRegistry registry) {
        super(registry, false);
    }

    public void setEsmSessionFactory(EsmSessionFactory esmSessionFactory) {
        this.esmSessionFactory = esmSessionFactory;
    }
    public void setAnnotationClass(Class<? extends Annotation> annotationClass) {
        this.annotationClass = annotationClass;
    }

    public void setMarkerInterface(Class<?> markerInterface) {
        this.markerInterface = markerInterface;
    }

    private static Environment getOrCreateEnvironment(BeanDefinitionRegistry registry) {
        Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
        return (Environment)(registry instanceof EnvironmentCapable?((EnvironmentCapable)registry).getEnvironment():new StandardEnvironment());
    }

    /**
     * 配置父扫描器以搜索正确的界面。它可以搜索所有接口，或仅针对那些扩展markerInterface 或/和 用annotationClass注释的接口
     */
    public void registerFilters() {
        boolean acceptAllInterfaces = true;
        //如果指定，请使用给定的注释 and / or  标记界面
        if (this.annotationClass != null) {
            addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
            acceptAllInterfaces = false;
        }

        // override AssignableTypeFilter to ignore matches on the actual marker interface
        if (this.markerInterface != null) {
            addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
                @Override
                protected boolean matchClassName(String className) {
                    return false;
                }
            });
            acceptAllInterfaces = false;
        }

        if (acceptAllInterfaces) {
            // default include filter that accepts all classes
            addIncludeFilter(new TypeFilter() {
                public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
                    return true;
                }
            });
        }

        // exclude package-info.java
        addExcludeFilter(new TypeFilter() {
            public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
                String className = metadataReader.getClassMetadata().getClassName();
                return className.endsWith("package-info");
            }
        });
    }
    /**
     * 扫描包下所有注册的类，然后将类对象进行后处理，增加esmSessionFacotry 等信息
     */
    @Override
    public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        //得到这个包下的bean的句柄
        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
        if (beanDefinitions.isEmpty()) {
            logger.warn("No ESM mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
        } else {
            //循环bean的句柄
            for (BeanDefinitionHolder holder : beanDefinitions) {
                //通用的bean的定义
                GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
                // 映射接口是bean的原始类， 但是，实际的bean类是EsmMapperFactoryBean
                //就是将接口映射成 EsmMapperFactoryBean
                definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
                definition.setBeanClass(EsmMapperFactoryBean.class);
                boolean explicitFactoryUsed = false;
                if (this.esmSessionFactory != null) {
                    definition.getPropertyValues().add("esmSessionFactory", this.esmSessionFactory);
                    explicitFactoryUsed = true;
                }
                //判断是否使用了  esmSessionFactory
                if (!explicitFactoryUsed) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Enabling autowire by type for EsmMapperFactoryBean with name '" + holder.getBeanName() + "'.");
                    }
                    //定义的bean 自动注入
                    definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
                }
            }
        }
        return beanDefinitions;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        return (beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
        if (super.checkCandidate(beanName, beanDefinition)) {
            return true;
        } else {
            logger.warn("Skipping MapperFactoryBean with name '" + beanName
                    + "' and '" + beanDefinition.getBeanClassName() + "' mapperInterface"
                    + ". Bean already defined with the same name!");
            return false;
        }
    }
}
